/*
  AdViEmulator - AdventureVision emulator
  Copyright (C) 2012-2013  JustBurn

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "mainwindow.h"
#include "cpuwindow.h"
#include "ui_cpuwindow.h"

enum {
	HEXIO_PC, HEXIO_A, HEXIO_PSW,
	HEXIO_R0, HEXIO_R1, HEXIO_R2, HEXIO_R3,
	HEXIO_R4, HEXIO_R5, HEXIO_R6, HEXIO_R7,
	HEXIO_XR0, HEXIO_XR1, HEXIO_XR2, HEXIO_XR3,
	HEXIO_XR4, HEXIO_XR5, HEXIO_XR6, HEXIO_XR7,
	COMBO_STACK,
	CHECK_F0, CHECK_F1, CHECK_AC, CHECK_C, CHECK_MB,
	RADIO_RB0, RADIO_RB1
};

#define CONN_HEXIO(c, e) { connect(c, SIGNAL(valueChanged(int)), this, SLOT(hexioChanged(int))); c->setProperty("type", QVariant(e)); }
#define CONN_COMBO(c, e) { connect(c, SIGNAL(currentIndexChanged(int)), this, SLOT(comboChanged(int))); c->setProperty("type", QVariant(e)); }
#define CONN_ABUTTON(c, e) { connect(c, SIGNAL(toggled(bool)), this, SLOT(abuttonChanged(bool))); c->setProperty("type", QVariant(e)); }

CPUWindow::CPUWindow(QWidget *parent, AdViEmulator *emu)
	: BaseViewWindow(parent)
	, refreshTmr(new QTimer(this))
	, ui(new Ui::CPUWindow)
{
	ui->setupUi(this);
	avemu = emu;
	connect(refreshTmr, SIGNAL(timeout()), this, SLOT(refreshWindow()));
	connect(parent, SIGNAL(emuStateChanged(int)), this, SLOT(emuStateChanged(int)));
	connect(ui->pushStart, SIGNAL(clicked()), parent, SLOT(OnMenuStartEmu()));
	connect(ui->pushStop, SIGNAL(clicked()), parent, SLOT(OnMenuStopEmu()));
	connect(ui->pushStep, SIGNAL(clicked()), parent, SLOT(OnMenuCPUStepEmu()));
	connect(ui->pushReset, SIGNAL(clicked()), parent, SLOT(OnMenuResetEmu()));
	refreshTmr->start(500);
	ui->boxView->attachEmu(emu, ui->scrollView);
	refreshRegisters();
	setAddress = -1;
	// Connect signals
	ui->linePC->setDigits(3);
	ui->linePC->setMaximum(0xFFF);
	CONN_HEXIO(ui->linePC, HEXIO_PC);
	CONN_HEXIO(ui->lineA, HEXIO_A);
	CONN_HEXIO(ui->linePSW, HEXIO_PSW);
	CONN_HEXIO(ui->lineR0, HEXIO_R0);
	CONN_HEXIO(ui->lineR1, HEXIO_R1);
	CONN_HEXIO(ui->lineR2, HEXIO_R2);
	CONN_HEXIO(ui->lineR3, HEXIO_R3);
	CONN_HEXIO(ui->lineR4, HEXIO_R4);
	CONN_HEXIO(ui->lineR5, HEXIO_R5);
	CONN_HEXIO(ui->lineR6, HEXIO_R6);
	CONN_HEXIO(ui->lineR7, HEXIO_R7);
	CONN_HEXIO(ui->lineXR0, HEXIO_XR0);
	CONN_HEXIO(ui->lineXR1, HEXIO_XR1);
	CONN_HEXIO(ui->lineXR2, HEXIO_XR2);
	CONN_HEXIO(ui->lineXR3, HEXIO_XR3);
	CONN_HEXIO(ui->lineXR4, HEXIO_XR4);
	CONN_HEXIO(ui->lineXR5, HEXIO_XR5);
	CONN_HEXIO(ui->lineXR6, HEXIO_XR6);
	CONN_HEXIO(ui->lineXR7, HEXIO_XR7);
	CONN_COMBO(ui->comboPSWStack, COMBO_STACK);
	CONN_ABUTTON(ui->checkF0, CHECK_F0);
	CONN_ABUTTON(ui->checkF1, CHECK_F1);
	CONN_ABUTTON(ui->checkAC, CHECK_AC);
	CONN_ABUTTON(ui->checkC, CHECK_C);
	CONN_ABUTTON(ui->checkMB, CHECK_MB);
	CONN_ABUTTON(ui->radioRB0, RADIO_RB0);
	CONN_ABUTTON(ui->radioRB1, RADIO_RB1);
	QTimer::singleShot(0, this, SLOT(firstWindow()));
}

CPUWindow::~CPUWindow()
{
	refreshTmr->stop();
	delete refreshTmr;
	delete ui;
}

void CPUWindow::setCPUAddress(int addr)
{
	setAddress = addr;
}

void CPUWindow::firstWindow()
{
	ui->boxView->repaint();
	if (setAddress == -1) {
		ui->boxView->moveCursor(avemu->cpu.pc.w, true);
	} else {
		ui->boxView->moveCursor(setAddress, true);
	}
}

void CPUWindow::refreshWindow(void)
{
	if (avemu->IsRunning() && (ui->checkFollowPC->checkState() == Qt::Checked)) {
		ui->boxView->moveCursor(avemu->cpu.pc.w);
		refreshRegisters();
	}
	ui->boxView->update();
}

void CPUWindow::refreshRegisters(void)
{
	ui->linePC->setValue(avemu->cpu.pc.w);
	ui->lineA->setValue(avemu->cpu.accu);
	ui->linePSW->setValue(avemu->cpu.psw.b);
	ui->lineR0->setValue(avemu->IRAM[0]);
	ui->lineR1->setValue(avemu->IRAM[1]);
	ui->lineR2->setValue(avemu->IRAM[2]);
	ui->lineR3->setValue(avemu->IRAM[3]);
	ui->lineR4->setValue(avemu->IRAM[4]);
	ui->lineR5->setValue(avemu->IRAM[5]);
	ui->lineR6->setValue(avemu->IRAM[6]);
	ui->lineR7->setValue(avemu->IRAM[7]);
	ui->lineXR0->setValue(avemu->IRAM[24]);
	ui->lineXR1->setValue(avemu->IRAM[25]);
	ui->lineXR2->setValue(avemu->IRAM[26]);
	ui->lineXR3->setValue(avemu->IRAM[27]);
	ui->lineXR4->setValue(avemu->IRAM[28]);
	ui->lineXR5->setValue(avemu->IRAM[29]);
	ui->lineXR6->setValue(avemu->IRAM[30]);
	ui->lineXR7->setValue(avemu->IRAM[31]);
	ui->comboPSWStack->setCurrentIndex(avemu->cpu.psw.stack);
	ui->checkF0->setChecked(avemu->cpu.psw.f0);
	ui->checkF1->setChecked(avemu->cpu.psw.f1);
	ui->checkAC->setChecked(avemu->cpu.psw.ac);
	ui->checkC->setChecked(avemu->cpu.psw.c);
	ui->checkMB->setChecked(avemu->cpu.psw.mb);
	if (avemu->cpu.psw.rb == 0) ui->radioRB0->setChecked(true);
	if (avemu->cpu.psw.rb == 1) ui->radioRB1->setChecked(true);
}

void CPUWindow::emuStateChanged(int)
{
	if (ui->checkFollowPC->checkState() == Qt::Checked) {
		ui->boxView->moveCursor(avemu->cpu.pc.w);
	}
	refreshRegisters();
	refreshWindow();
}

void CPUWindow::hexioChanged(int value)
{
	switch(sender()->property("type").toInt()) {
	case HEXIO_PC: avemu->cpu.pc.w = value & 4095; break;
	case HEXIO_A: avemu->cpu.accu = value; break;
	case HEXIO_PSW: avemu->cpu.psw.b = value | 0x08; break;
	case HEXIO_R0: avemu->IRAM[0] = value; break;
	case HEXIO_R1: avemu->IRAM[1] = value; break;
	case HEXIO_R2: avemu->IRAM[2] = value; break;
	case HEXIO_R3: avemu->IRAM[3] = value; break;
	case HEXIO_R4: avemu->IRAM[4] = value; break;
	case HEXIO_R5: avemu->IRAM[5] = value; break;
	case HEXIO_R6: avemu->IRAM[6] = value; break;
	case HEXIO_R7: avemu->IRAM[7] = value; break;
	case HEXIO_XR0: avemu->IRAM[24] = value; break;
	case HEXIO_XR1: avemu->IRAM[25] = value; break;
	case HEXIO_XR2: avemu->IRAM[26] = value; break;
	case HEXIO_XR3: avemu->IRAM[27] = value; break;
	case HEXIO_XR4: avemu->IRAM[28] = value; break;
	case HEXIO_XR5: avemu->IRAM[29] = value; break;
	case HEXIO_XR6: avemu->IRAM[30] = value; break;
	case HEXIO_XR7: avemu->IRAM[31] = value; break;
	}
	refreshRegisters();
	refreshWindow();
}

void CPUWindow::comboChanged(int index)
{
	switch(sender()->property("type").toInt()) {
	case COMBO_STACK: avemu->cpu.psw.stack = index; break;
	}
	refreshRegisters();
	refreshWindow();
}

void CPUWindow::abuttonChanged(bool checked)
{
	switch(sender()->property("type").toInt()) {
	case CHECK_F0: avemu->cpu.psw.f0 = checked ? 1 : 0; break;
	case CHECK_F1: avemu->cpu.psw.f1 = checked ? 1 : 0; break;
	case CHECK_AC: avemu->cpu.psw.ac = checked ? 1 : 0; break;
	case CHECK_C: avemu->cpu.psw.c = checked ? 1 : 0; break;
	case CHECK_MB: avemu->cpu.psw.mb = checked ? 1 : 0; break;
	case RADIO_RB0: if (checked) avemu->cpu.psw.rb = 0; break;
	case RADIO_RB1: if (checked) avemu->cpu.psw.rb = 1; break;
	}
	refreshRegisters();
	refreshWindow();
}

void CPUWindow::findPC(void)
{
	ui->boxView->moveCursor(avemu->cpu.pc.w, true);
	ui->boxView->update();
}

void CPUWindow::goToAddress(void)
{
	bool okConv;
	int number = ui->lineAddress->text().toInt(&okConv, 16);
	if (okConv) {
		ui->boxView->moveCursor(number, true);
		ui->boxView->update();
	}
}
